6.1 Clases y Objetos
En Python, una clase es una plantilla que define cómo serán los objetos que se creen a partir de ella.
Dentro de una clase se especifican los atributos (datos que guarda el objeto) y los métodos (acciones o comportamientos que puede realizar).
La idea es parecida a un plano de una casa:
- La clase sería el plano (define cómo son las casas en general).
- Cada objeto sería una casa concreta construida a partir de ese plano.
1️⃣ Definir clases y crear objetos
🟩 Sintaxis básica para definir una clase
La sintaxis general para crear una clase en Python es:
class NombreClase:
def __init__(self, parametros):
# Atributos de instancia (datos del objeto)
self.atributo1 = valor_inicial1
self.atributo2 = valor_inicial2
def metodo_ejemplo(self, otros_parametros):
# Comportamiento del objeto
# (instrucciones que se ejecutan al llamar al método)
...
class→ palabra clave para indicar que se esta definiendo una clase.NombreClase→ nombre de la clase (por convención, empieza por mayúscula y se escribe en CamelCase).__init__→ método especial llamado constructor. Se ejecuta automáticamente al crear el objeto.self→ referencia al propio objeto. Permite acceder a sus atributos y métodos.método_ejemplo→ método normal, que define una acción que los objetos pueden realizar.
class Persona:
def __init__(self, nombre, edad):
self.nombre = nombre # atributo de instancia
self.edad = edad # atributo de instancia
def saludar(self):
print(f"Hola, soy {self.nombre} y tengo {self.edad} años.")
- La clase
Personadefine que todas las personas tendran unnombrey unaedad. - El método
saludar()describe un comportamiento común a todas las personas.
🟦 Objetos: instancias de una clase
Un objeto es una instancia concreta de una clase. Cada objeto tiene sus propios valores para los atributos, aunque comparta los mismos métodos definidos en la clase.
p1 = Persona("Ana", 25)
p2 = Persona("Luis", 30)
print(p1.nombre) # Ana
print(p2.edad) # 30
p1.saludar() # Hola, soy Ana y tengo 25 años.
p2.saludar() # Hola, soy Luis y tengo 30 años.
p1yp2son objetos de la clasePersona.- Ambos tienen los mismos atributos (
nombre,edad) pero con valores distintos. - Los dos pueden usar el método
saludar(), que se comporta de forma personalizada según los datos de cada objeto.
2️⃣ El constructor y el parámetro self
En muchas clases es necesario que los objetos nazcan con unos datos mínimos: nombre, edad, saldo, color, etc.
Para ello se utiliza un método especial llamado constructor, que en Python se implementa con __init__().
🟩 El constructor __init__
El método __init__ se ejecuta automáticamente cada vez que se crea un objeto de la clase. Sirve para inicializar los atributos del nuevo objeto. No devuelve nada (Python asume que el resultado es None).
class NombreClase:
def __init__(self, parametros):
# inicialización de atributos
self.atributo1 = valor1
self.atributo2 = valor2
Siempre que se escriba
NombreClase(...), Python creará un nuevo objeto y llamará a__init__para configurarlo.
🟦 El parámetro self
El primer parámetro de __init__ (y de cualquier método de instancia) se llama por convención self y representa al propio objeto.
selfpermite acceder a los datos y métodos del objeto.- No hace falta pasar
selfal llamar al método: Python lo hace de forma automática.
class Coche:
def __init__(self, marca, color):
self.marca = marca # atributo del objeto
self.color = color # atributo del objeto
auto = Coche("Toyota", "rojo")
print(auto.marca) # Toyota
print(auto.color) # rojo
Aunque el nombre self no es obligatorio (podría llamarse este u objeto), es una convención estricta en Python y se recomienda usarlo siempre.
🟨 Constructores con valores por defecto
Es posible definir parámetros con valores por defecto en __init__.
Esto permite crear objetos con más o menos información según convenga.
class Persona:
def __init__(self, nombre, edad=18):
self.nombre = nombre
self.edad = edad
p1 = Persona("Ana", 25)
p2 = Persona("Luis") # edad tomará el valor por defecto: 18
print(p1.edad) # 25
print(p2.edad) # 18
🟧 Errores frecuentes con self
-
Olvidar el parámetro
selfen la definición:class Persona:
def __init__(nombre, edad): # ❌ Falta self
self.nombre = nombre # NameErrorAquí Python interpretara que
nombreesself, yselfno estará definido. -
No usar
selfal asignar atributos:class Persona:
def __init__(self, nombre):
nombre = nombre # ❌ no se guarda en el objetoEsta instrucción solo asigna la variable local
nombrea si misma. Lo correcto es:self.nombre = nombre # ✅ atributo del objeto
3️⃣ Atributos de clase y de instancia
En una clase de Python se pueden definir dos tipos principales de atributos:
| Tipo de atributo | Qué representa | Dónde se define | Ejemplo |
|---|---|---|---|
| De instancia | Propio de cada objeto concreto | Dentro del método __init__ | self.nombre |
| De clase | Común a todos los objetos de la clase | Directamente en la clase | especie = "Humano" |
Los atributos de instancia guardan información particular de cada objeto (por ejemplo, el nombre de una persona).
Los atributos de clase representan un dato compartido por todas las instancias de esa clase (por ejemplo, que todas las personas son humanas).
class Persona:
especie = "Humano" # Atributo de clase (común a todas las personas)
def __init__(self, nombre):
self.nombre = nombre # Atributo de instancia (propio de cada objeto)
p1 = Persona("Ana")
p2 = Persona("Luis")
print(p1.especie) # Humano
print(p2.especie) # Humano
print(p1.nombre) # Ana
print(p2.nombre) # Luis
🟩 Cómo accede Python a los atributos
Cuando se escribe p1.especie, Python busca el atributo en este orden:
- Primero en los atributos de instancia del objeto
p1. - Si no lo encuentra, lo busca en los atributos de la clase
Persona.
Por eso es habitual acceder a los atributos de clase también a través de los objetos, aunque la forma recomendada para dejarlo más claro es usar el nombre de la clase:
print(Persona.especie) # Mejor para dejar claro que es algo común a la clase
Es importante distinguir entre:
- Cambiar el atributo de clase para todos los objetos.
- Crear o modificar un atributo de instancia solo para un objeto concreto.
class Persona:
especie = "Humano"
def __init__(self, nombre):
self.nombre = nombre
p1 = Persona("Ana")
p2 = Persona("Luis")
# Cambiar el atributo de CLASE
Persona.especie = "Humano terrestre"
print(p1.especie) # Humano terrestre
print(p2.especie) # Humano terrestre
# Sobrescribir SOLO para p1 (crea un atributo de INSTANCIA llamado 'especie')
p1.especie = "Extraterrestre amistoso"
print(p1.especie) # Extraterrestre amistoso
print(p2.especie) # Humano terrestre
print(Persona.especie) # Humano terrestre
🟦 ¿Cuándo usar cada tipo de atributo?
Atributos de instancia (dentro de __init__, usando self):
- Datos que cambian de un objeto a otro.
- Ejemplos:
nombre,edad,saldo,color.
Atributos de clase (definidos directamente en la clase):
- Información común a todas las instancias.
- Valores constantes o configuraciones generales.
- Contadores o estadísticas compartidas por todos los objetos.
class Alumno:
total_alumnos = 0 # atributo de clase
def __init__(self, nombre):
self.nombre = nombre # atributo de instancia
Alumno.total_alumnos += 1 # se incrementa al crear cada objeto
a1 = Alumno("Ana")
a2 = Alumno("Luis")
print(Alumno.total_alumnos) # 2
4️⃣ Métodos de instancia
Los métodos de instancia son funciones definidas dentro de una clase que describen las acciones que puede realizar un objeto.
Se llaman “de instancia” porque siempre trabajan con una instancia concreta (un objeto) y reciben como primer parámetro self, que representa a ese objeto.
En cierto modo:
- Una función normal trabaja con los datos que se le pasan.
- Un método trabaja con los datos que pertenecen a un objeto (
self.atributo).
La forma general de definir un método de instancia es:
class NombreClase:
def nombre_metodo(self, otros_parametros):
# bloque de instrucciones
...
def→ define el método.self→ referencia al propio objeto.otros_parámetros→ datos adicionales que el método necesita para trabajar.
class Persona:
def __init__(self, nombre):
self.nombre = nombre
def saludar(self):
print(f"Hola, soy {self.nombre}")
p = Persona("Ana")
p.saludar()
5️⃣ Organización del código
Las clases viven en “módulos” (archivos .py), y el main.py debería ser solo el punto de entrada que las usa, no donde vive toda la lógica.
Luego según el tamaño del proyecto, cambiamos la estrategia.
En cuanto el programa tenga pocas clases emplearemos:
- Un archivo donde se ejecuta el programa (por ejemplo
main.py). - Un archivo donde se definen las clases (módulos).
mi_proyecto/
main.py # punto de entrada, usa las clases
modelos.py # clases principales
Cuando el proyecto crece, lo habitual es:
- Agrupar clases relacionadas en el mismo archivo (módulo).
- No hace falta una clase por archivo, salvo que sean muy grandes o importantes.
Ejemplo:
mi_proyecto/
main.py
personas.py # Persona, Alumno, Profesor...
cursos.py # Curso, Asignatura...
✅ Buenas prácticas al definir clases
- Usa nombres en singular y con mayúscula inicial para las clases (
Alumno,Coche,Factura). - Los nombres de métodos y atributos deben ir en minúsculas y con guiones bajos (
saludar,nombre_cliente). - Evita incluir demasiada lógica en el constructor
__init__.
Antes de escribir código, piensa qué clases necesitas y qué datos o acciones deben tener. Este diseño previo te permitirá construir programas más ordenados y fáciles de mantener.
⚔️ Actividad Práctica: La Arena
En esta actividad pondremos en práctica todo lo aprendido sobre clases, objetos, constructores y atributos mediante la creación de un simulador de combate RPG por niveles.
🏟️ 1. La arena de entrenamiento
En un videojuego de rol, los personajes deben entrenar en una arena de combate antes de enfrentarse a enemigos reales. Cada personaje tiene unas características básicas como su nombre, la cantidad de vida que posee y la fuerza de sus ataques.
La academia de entrenamiento te ha encargado desarrollar un programa sencillo en Python, llamado personaje.py, que permita modelar a estos personajes y simular un combate básico entre dos de ellos.
Tu tarea consiste en definir una clase que represente a un personaje y utilizarla para crear dos combatientes que se ataquen durante varios turnos.
📋 Tarea a realizar
Se debe crear una clase llamada Personaje que represente a un combatiente del juego. Cada personaje debe tener:
- Un nombre.
- Una cantidad de vida (no puede ser nunca negativa).
- Un valor de ataque.
Además, la clase debe incluir métodos que permitan:
- Comprobar si el personaje sigue con vida (
esta_vivo). - Recibir daño cuando es atacado. Cuando un personaje recibe daño, existe una probabilidad de un 10% de que su armadura absorba el golpe, evitando que pierda vida.
- Atacar a otro personaje. Cuando un personaje ataca a otro, el daño producido no será fijo, sino un valor aleatorio entre 0 y el ataque máximo del atacante.
- Mostrar su estado actual (nombre, vida y ataque).
Una vez definida la clase, el programa deberá:
- Crear dos personajes distintos.
- Mostrar el estado inicial de ambos.
- Simular 3 turnos de combate, en los que los personajes se atacan de forma alterna.
- Mostrar el estado de los personajes tras cada ataque.
- Finalizar el combate antes de los 3 turnos si alguno de los personajes se queda sin vida.
== COMBATE ==
Heroe | Vida: 30 | Ataque: 6
Goblin | Vida: 22 | Ataque: 4
-- Turno 1 --
Heroe ataca a Goblin y causa 5 de daño
Goblin | Vida: 17 | Ataque: 4
Goblin ataca a Heroe y causa 3 de daño
La armadura de Heroe ha absorbido el daño
Heroe | Vida: 30 | Ataque: 6
🎒 2. El equipo del aventurero
En la arena de entrenamiento, los personajes no solo luchan con su fuerza, también dependen del equipo y los objetos que llevan consigo. Pociones, armas o amuletos pueden marcar la diferencia durante un combate.
La academia de entrenamiento quiere ampliar el sistema de combate desarrollado anteriormente para que los personajes puedan almacenar y gestionar objetos mediante un inventario.
Tu tarea consiste en crear nuevas clases que representen los objetos del juego y un inventario, y asociar dicho inventario a cada personaje. Desarrolla el archivo inventario.py.
📋 Tarea a realizar
Se deben crear las siguientes clases:
- Clase Item: Representa un objeto genérico del juego. Cada objeto debe tener un nombre y un peso.
- Clase Inventario: Representa el inventario de un personaje. Debe incluir:
- Una colección de objetos (
Item). - Un método para añadir un objeto al inventario.
- Un método para calcular el peso total del inventario.
- Un método para mostrar el contenido del inventario de forma ordenada.
- Una colección de objetos (
- Modificación de la clase Personaje:
- Cada personaje tenga un inventario propio. El inventario se cree automáticamente al crear el personaje.
Una vez definidas las clases, el programa deberá crear un personaje, añadir al menos tres objetos distintos a su inventario y mostrar el estado completo (personaje + contenido del inventario + peso total).
Heroe | Vida: 30 | Ataque: 6
Inventario:
1. Pocion pequeña (0.2 kg)
2. Espada vieja (2.5 kg)
3. Amuleto magico (0.1 kg)
Peso total del inventario: 2.8 kg
👿 3. El control de la arena
La academia de entrenamiento necesita llevar un registro global de todos los enemigos que han sido creados para las sesiones de combate. Este control es importante para analizar la dificultad de la arena y ajustar el número de combates que realizan los aspirantes.
Para ello, se ha decidido ampliar el sistema de personajes incorporando un contador común que permita saber cuántos enemigos se han generado durante la ejecución del programa.
Tu tarea consiste en crear una clase que represente a los enemigos de la arena y mantener un registro del número total de enemigos creados en el archivo enemigo.py.
📋 Tarea a realizar
Se debe crear una clase llamada Enemigo que represente a los adversarios del juego. Cada enemigo debe tener:
- Un tipo o nombre identificativo.
- Una cantidad de vida.
- Un valor de ataque.
Además, la clase debe llevar un registro global (atributo de clase) del número total de enemigos creados desde que se inicia el programa.
El programa deberá crear varios enemigos de distintos tipos, aumentar automáticamente el contador global y finalmente mostrar el número total de enemigos creados junto al estado de cada uno.
Enemigos creados en la arena: 5
1. Goblin | Vida: 20 | Ataque: 4
2. Lobo | Vida: 18 | Ataque: 5
3. Bandido | Vida: 24 | Ataque: 6
4. Esqueleto | Vida: 22 | Ataque: 5
5. Ogro | Vida: 35 | Ataque: 8
🏆 4. El desafío final
Tras las sesiones de entrenamiento iniciales, la academia pone a prueba a los aspirantes en una arena real. En esta fase, el personaje del jugador deberá enfrentarse a varios enemigos utilizando todo lo desarrollado anteriormente: combate, inventario y control de enemigos.
Se debe crear un archivo principal llamado arena.py que utilice las clases desarrolladas en los bloques anteriores.
📋 Tarea a realizar
El programa deberá seguir el siguiente flujo general:
- Crear un personaje principal que representará al jugador.
- Crear varios enemigos utilizando la clase
Enemigo. - Enfrentar al jugador contra los enemigos uno por uno, en el orden en que fueron creados.
- Durante cada combate:
- El jugador y el enemigo se atacan de forma alterna con daño aleatorio.
- Existe la probabilidad de que la armadura absorba el daño.
- El combate termina cuando uno se queda sin vida.
- Si el jugador vence, puede recibir un objeto como recompensa y pasa al siguiente.
- Si el jugador pierde, la sesión de la arena finaliza.
Al finalizar, se deberá mostrar el número total de personajes enemigos generados (usando el atributo de clase) y el estado final del héroe.
== INICIO DE LA ARENA ==
El aspirante Heroe entra en la arena preparado para el combate.
Objetos iniciales: Pocion (0.5 kg), Espada (2.0 kg)
Total de enemigos a batir: 3
-- COMBATE 1: Heroe vs Goblin --
Heroe | Vida: 30 | Ataque: 6
Goblin | Vida: 15 | Ataque: 3
...
¡Heroe ha vencido a Goblin!
¡Has obtenido un nuevo objeto: Escudo pequeño (1.5 kg)!
-- COMBATE 2: Heroe vs Lobo --
...
== FIN DEL ENTRENAMIENTO ==
Heroe ha completado todos los combates con 12 puntos de vida restantes.
Total de personajes enemigos generados: 3